package com.iflytek.esb.service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.iflytek.esb.domain.Message;
import com.iflytek.esb.domain.PersistenceMessage;
import com.iflytek.esb.exceptions.RetryableException;
import com.iflytek.esb.util.Consts;
import com.opensymphony.workflow.WorkflowException;

@Service("backendService")
public class BackendService {
	
	private static final Logger LOG = Logger.getLogger(BackendService.class);
	
	@Autowired
	private QueueService queueService;
	
	@Autowired
	private WorkflowService workflowService;
	
	@Autowired
	private MessageService messageService;

	@Transactional
	public void execute() throws Exception {
		//得到消息
		Map<String, Object> q = queueService.deQueue();
		if (q == null) {
			return;
		}
		
		long entryId = 0;
		if (q.containsKey(Consts.MQ_KEY_ENTRY)) {
			entryId = Long.parseLong(String.valueOf(q.get(Consts.MQ_KEY_ENTRY)));
		}
		//long messageId = 0;
		//if (q.containsKey(Consts.MQ_KEY_MESSAGE)) {
		//	messageId = Long.parseLong(String.valueOf(q.get(Consts.MQ_KEY_MESSAGE)));
		//}
		
		try {
			entryId = processQuene(q);
		}
		catch (Exception e) {
			LOG.error("<Failure exception happen, entry id: " + entryId + ">", e);
			
			//String entryName = workflowService.getWorkflowName(entryId);
			//traceService.traceFailure(entryId, entryName, messageId, getExceptionMessage(e));
			
			throw e;
		}
	}

	private long processQuene(Map<String, Object> q) throws WorkflowException {
		long entryId = 0;
		long messageId = 0;
	
		//得到消息内容
		int tryCount = Integer.parseInt(String.valueOf(q.get(Consts.MQ_KEY_TRY)));
		String type = String.valueOf(q.get(Consts.MQ_KEY_TYPE));
	
		try {
			//判断消息的类型，采用不同的处理规则
			if (StringUtils.equals(type, Consts.MQ_KEY_MESSAGE)) {
				messageId = Long.parseLong(String.valueOf(q.get(Consts.MQ_KEY_MESSAGE)));
				processMessage(messageId);
			}
			else if (StringUtils.equals(type, Consts.MQ_KEY_ENTRY)) {
				entryId = Long.parseLong(String.valueOf(q.get(Consts.MQ_KEY_ENTRY)));
				processEntry(entryId);
			}
			return entryId;
		}
		catch (RetryableException e) {
			//捕捉到可重试异常，计算下次重试时间
			int interval = e.getIntervalInSeconds();
			int maxTry = e.getMaxTry();
			
			//如果没有超出最大重试次数，加入队列下次执行
			if (tryCount < maxTry) {
				//重试次数+1
				q.remove(Consts.MQ_KEY_TRY);
				q.put(Consts.MQ_KEY_TRY, String.valueOf(tryCount + 1));
				Date t = new Date(new Date().getTime() + (interval * 1000));
				queueService.inQueue(q, t);
				
				return entryId;
			}
			else {
				//超出了最大重试次数，抛出异常
				throw new WorkflowException("Exceeded max try times", e);
			}
		}
	}

	private void processEntry(long entryId) throws WorkflowException {
	
		LOG.info("<process workflow: " + entryId + ">");
		
		//寻找工作流相关的消息
		List<PersistenceMessage> messages = messageService.getMessageByEntry(entryId);
		if (messages.size() == 0 || messages.get(0) == null) {
			return;
		}
		
		//执行工作流
		workflowService.executeWorkflow(entryId, messages.get(0));
		
		//将待办事务放到消息队列中
		putNextTask(entryId, messages.get(0));
	}

	private long processMessage(long messageId) throws WorkflowException {
		
		LOG.info("<process message: " + messageId + ">");
		
		PersistenceMessage message = messageService.getMessageById(messageId);
		
		//启动工作流
		long id = workflowService.startWorkflow(message);
	
		//将待办事务放到消息队列中
		putNextTask(id, message);
		
		return id;
	}

	private void putNextTask(long entryId, Message message) {
		
		int[] actions = workflowService.getAvailableActions(entryId, message);
		if (actions != null && actions.length > 0) {
			//有待办事项, 放入队列
			Map<String, Object> datas = new HashMap<String, Object>();
			datas.put(Consts.MQ_KEY_TYPE, Consts.MQ_KEY_ENTRY);
			datas.put(Consts.MQ_KEY_ENTRY, Long.valueOf(entryId));
			datas.put(Consts.MQ_KEY_TRY, 1);
			
			queueService.inQueue(datas, new Date(0));
		}
	}

//	private String getExceptionMessage(Exception e) {
//		if (e == null) {
//			return StringUtils.EMPTY;
//		}
//		StringWriter errors = new StringWriter();
//		e.printStackTrace(new PrintWriter(errors));
//		return e.getMessage() + "\r\n" + errors.toString();
//	}
}
